package reversi;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
/**
 * Klasa <code>ReversiConsoleEngine</code> stanowi silnik gry Reversi oparty na operacjach konsolowych.
 * Dla tak prostego przykładu wystarczyło napisanie go jako klasy dziedziczącej {@link ReversiBoard}
 * 
 * @author Sebastian Gabor
 * @version 0.1
 *
 */

public class ReversiConsoleEngine extends ReversiBoard  {
	
	/**
	 * ReversiConsoleEngine, mimo że gra umożliwia tworzenie większych plansz. Silnik konsolowy wymusza
	 * plansze o rozmiarze 8 na 8.
	 * 
	 * @param aSizeX - właściwie może być tylko 8
	 * @param aSizeY - właściwie może być tylko 8
	 * @throws BoardException - dla wszelkich innych rozmiarów plansz.
	 */

	public ReversiConsoleEngine(int aSizeX, int aSizeY) throws BoardException {	
		super(aSizeX, aSizeY);
		if (aSizeX!=8 && aSizeY!=8){
			throw new BoardException("Niestety wersja konsolowa nie jest zdolna obsługiwać różne wariacje");
		}
		rdg=new ReversiDataGetter();
		asServer=false;
	}
	

	
	

	/**
	 * Pozwala na "jako takie" oczyszczenie ekranu z nadmiaru plansz.
	 */
	public void clearScreen(){
		String clearScreenCommand = null;
		// wygląda bardzo brzydko:
		if( System.getProperty( "os.name" ).startsWith( "Window" ) ){
		    clearScreenCommand = "cls";
		    try {
				Runtime.getRuntime().exec( clearScreenCommand );
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

		}
		else{
			for (int i=0; i<40; i++)
				System.out.println(" ");
			

		}

		
		
	}
	/**
	 * Wyświetla planszę gry na ekranie.
	 */
	public void showBoard(){
		if (getCurrentTurn()==GREEN)
			System.out.println("Ruch: ZIELONY (*)");
		else if(getCurrentTurn()==RED)
			System.out.println("Ruch: CZERWONY (@)");
		System.out.print("  ");
		for (byte i=0; i<getSizeX(); i++)
			System.out.print(i+1+" ");
		System.out.println();
			
		for (int i=0; i<getSizeX(); i++){
			
			System.out.print((char)(i+97)+" ");
			for (int j=0; j<getSizeY(); j++){
				
				if (getFieldStatus(i, j)==EMPTY)
					System.out.print(". ");
				else if (getFieldStatus(i, j)==RED)
					System.out.print("@ ");
						
				else if (getFieldStatus(i, j)==GREEN)
					System.out.print("* ");
			}
			System.out.println("");
			
		}
				
	
	}
	
	
	/**
	 * Metoda umożliwiająca grę po sieci. Zawiera główną pętlę. Powinna być wywoływana w metodzie main.
	 */
	public void netPlay(boolean start) throws BoardException{

		BoardLocation bl=null;
		boolean done=false;
		String last_sent="";
		String s="";
		while(!checkIfFastEnd()){
			if (!start){
				done=false;
				setAllPossibilities(getCurrentTurn());			
				clearScreen();
				showBoard();
			
				//System.out.print("Tutaj:");
				s=rdg.getLocation();				
				bl=new BoardLocation(s);				
				while (!isMovePossible(bl)){
					//System.out.print("Here");
					s=rdg.getLocation();				
					bl=new BoardLocation(s);	
					
				}
				last_sent=s;
				sendToServer("DATA"+game_name+";"+s+";"+game_pass);
				getFromServer();
					
				

				
			
				if (!getNextMove().isEmpty()){
				
					while (!done){	
						
						for (BoardLocation b : getNextMove()) {
							if (b.equalsWithoutStatus(bl)){
								setCoin(bl.getX(), bl.getY(), getCurrentTurn());
								swapCoins(new BoardLocation(bl.getX(), bl.getY(), getCurrentTurn()));
							
								endTurn();																
								break;
							}						
						}
						
						done=true;
					}
				}
				else{
					endTurn();
					setAllPossibilities(getCurrentTurn());
					if (getNextMove().isEmpty())
						break;
				
				
				}
				
			}
			
			clearScreen();
			showBoard();
			start=false;
			done=false;
			setAllPossibilities(getCurrentTurn());			
			clearScreen();
			showBoard();
			
			sendToServer("CHECK"+game_name+";"+game_pass);
			String reply;
			reply=getFromServer();
			
			while (!reply.startsWith("OKDATA")&&!reply.equals(last_sent)){
				sendToServer("CHECK"+game_name+";"+game_pass);
				reply=getFromServer();
			
			
			}
			reply=reply.substring("OKDATA".length());
			bl=new BoardLocation(reply);

			setCoin(bl.getX(), bl.getY(), getCurrentTurn());
			
			swapCoins(new BoardLocation(bl.getX(), bl.getY(), getCurrentTurn()));

			clearScreen();
			showBoard();
			endTurn();

		}
		byte winner=findWinner();
		showBoard();
		if (winner==RED){
			System.out.println("Wygrał czerwony");
			sendToServer("ENDGAME"+game_name+";"+game_pass);
		}
		else if (winner==GREEN){
			System.out.println("Wygrał zielony");
			sendToServer("ENDGAME"+game_name+";"+game_pass);
		}
		else
			System.out.println("REMIS");
			sendToServer("ENDGAME"+game_name+";"+game_pass);
	}
	/**
	 * Metoda umożliwiająca samą grę. Zawiera główną pętlę. Powinna być wywoływana w metodzie main.
	 */	
	public void play(){
 
		BoardLocation bl=null;
		boolean done=false;
		while(!checkIfFastEnd()){
			done=false;
			setAllPossibilities(getCurrentTurn());			
			clearScreen();
			showBoard();
			
			bl=rdg.getLocation(getCurrentTurn());
			
			if (!getNextMove().isEmpty()){
				
				while (!done){	
					
					for (BoardLocation b : getNextMove()) {
						if (b.equals(bl)){
							setCoin(bl.getX(), bl.getY(), getCurrentTurn());
							//System.out.println(getMovePoints(new BoardLocation(bl.getX(), bl.getY(), getCurrentTurn())));
							//System.out.print(bl.getX()+" "+bl.getY());
							swapCoins(new BoardLocation(bl.getX(), bl.getY(), getCurrentTurn()));
							
							endTurn();																
							break;
						}						
					}				
					done=true;
				}
			}
			else{
				endTurn();
				setAllPossibilities(getCurrentTurn());
				if (getNextMove().isEmpty())
					break;
				
				
			}
						
			
		}
		byte winner=findWinner();
		showBoard();
		if (winner==RED){
			System.out.println("Wygrał czerwony");
		}
		else if (winner==GREEN)
			System.out.println("Wygrał zielony");
		else
			System.out.println("REMIS");
		
	}
	
	/**
	 * Zwraca gotowy obiekt ReversiDataGetter
	 * @return używany obiekt ReversiDataGetter 
	 */
	public ReversiDataGetter getRDG(){
		return rdg;
	}
	
	/**
	 * Tworzy nową planszę ReversiBoard na podstawie danych zawartych w aktualnej sesji
	 * 
	 * @return nowy obiekt typu ReversiBoard
	 */
	
	public ReversiBoard getReversiBoard(){
		ReversiBoard rb =new ReversiBoard(8, 8);
		for (int i=0;i<8;i++)
			for (int j=0;j<8;j++)	
					rb.setCoin(i, j, this.getFieldStatus(i, j));
		rb.setMovePossible(this.isMovePossible());
		if (rb.getCurrentTurn()!=this.getCurrentTurn())
			endTurn();
		rb.checkIfFastEnd();
		return rb;
		
		
		
	}
	
	/**
	 * Funkcja wysyłająca dane na serwer.
	 * @param s komunikat do wysłania na serwer.
	 */
	
	public void sendToServer(String s){

		out.println(s);
	}
	/**
	 * Funkcja czekająca na odbiór danych z serwera. 
	 * @return komunikat wysłany przez serwer
	 */
	public String getFromServer(){
		String inputLine;
		try {
			while ((inputLine = in.readLine()) != null) {				
				Thread.sleep(1000);
				//System.out.println(inputLine);
				return inputLine;
			}
		} catch (IOException e) {
			e.printStackTrace();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		return "";
	}
	/**
	 * Ustawia odpowiednio gniazda, by serwer działał jako klient gry sieciowej.
	 */
	public void setClient(){
		try {
			clientSocket = new Socket("localhost", 4444);

        out = new PrintWriter(clientSocket.getOutputStream(), true);
        in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}		
	}
	
	public void setGameNameAndPass(String agame_name, String apass){
		game_name=agame_name;
		game_pass=apass;
	}

	/**
	 * Główna metoda służąca odpaleniu samej gry.
	 * @param args
	 */
	public static void main(String[] args) {
		ReversiConsoleEngine rce;
		
		try {
			rce = new ReversiConsoleEngine(8, 8);
			if (args.length==0){
				
				rce.play();
			}
			else if (args.length==3){
				if (args[0].startsWith("-c")){
					rce.setClient();
					rce.sendToServer("CREATE"+args[1]+";"+args[2]);
					rce.setGameNameAndPass(args[1], args[2]);
					rce.netPlay(false); 
					
					
				}
				else if (args[0].startsWith("-p")){

					rce.setClient();
					rce.setGameNameAndPass(args[1], args[2]);
					rce.netPlay(true);

					
				}
				
			}
			
		}
			
		 catch (BoardException e) {
			e.printStackTrace();
		}
	}
	
	
	/**
	 * Obiekt ten służy do pobierania danych w programie.   
	 */
	private ReversiDataGetter rdg;
	ServerSocket serverSocket = null;
	Socket clientSocket=null;
    PrintWriter out = null;
    BufferedReader in = null;
    boolean asServer;
    String name=null;
    String game_name=null;
    String game_pass=null;


	
	

}
